test

3.1 自动内存管理相关概念

自动内存管理是指无需使用free操作符就可以自动回收废弃对象占用的内存的垃圾回收技术。其实,自动内存管理并不是什么新生事物,其发展史几乎与现代计算机科学一样长,最早出现于早期Lisp Machine中使用的引用计数方法。自那之后,在引用计数方法之外,又发展出几种不同的堆管理策略,到目前为止,大部分自动内存管理系统使用的都是引用跟踪技术,在执行垃圾回收时会沿着对象的引用关系遍历堆中对象以确定哪些需要回收,哪些需要保留。

在本章中,特指,在使用垃圾回收的环境中,所有用于存储对象的、非线程局部的内存空间(non-thread local memory)(译者注,Thread-local storage)。

3.1.1 自适应内存管理

正如前面章节介绍的,从优化的角度讲,程序运行过程中JVM的具体行为和运行时反馈信息是非常重要的,而JRockit的一大创举就是将基于运行时反馈的自适应优化从代码生成推广到所有的运行时子系统,本章所要介绍的内存管理系统就是自适应优化的受益者之一。

自适应内存管理是指主要通过运行时反馈来调整内存管理系统的行为。自适应内存管理是 自动内存管理的一个特例,而自动内存管理是指使用某些垃圾回收技术来管理内存,使用户无需再显式清理无用对象,内存管理系统会自动检测并释放这些对象占用的资源。

出于性能考虑,自适应内存管理必须要正确利用运行时反馈,这其中包括修正GC策略,自动缩放堆大小,以适当的时间间隔清理内存碎片,以及判断何时"stop the world"(译者注,后文使用STW代替)等。 Stop the world是指暂停正在执行的Java应用程序,转而执行垃圾回收某些操作。

3.1.2 自动内存管理的优点

自动内存管理最大的优点就是可以大大加快软件开发的速度。大家都知道,进行多线程编程时常常会出现内存分配错误、缓冲区溢出和内存泄露等错误,而这些错误偏偏又很难调试排查,要是在程序运行一段时间后,由于发生了像释放了不该释放的对象这样的内存问题而导致程序崩溃可就是线上事故了。

Java编程语言的内建机制保证了使用Java编程时不会出现内存分配和缓冲区溢出问题,其中自动内存管理解决了内存分配问题,Java运行时系统则解决了缓冲区溢出的问题,例如,当发生数组越界的问题时会抛出ArrayIndexOutOfBoundsException异常。

不过,即使使用了垃圾回收技术,也难以彻底根除内存泄露,所以现代JVM都提供几种方法来检测是否存在内存泄露,而Java本身也可以帮助程序员绕过这个问题。就JRockit来说,JRockit Mission Control套件中包含了一个可以以较小的开销检测内存泄露的工具。由于JVM在执行垃圾回收的时候就会收集到很有用的信息,可以用于检测内存泄露。在本书的第10章会详细介绍 内存泄露检测仪

本书的作者们认为,内建的自动内存管理系统及更短的软件开发周期,是Java得以广泛推广的主要原因之一,而使用了自动内存管理后,复杂的服务器端应用程序也的确可以减少崩溃的次数。

此外,自适应内存管理可以根据应用程序的具体运行状态,适时的修正垃圾回收策略,例如改变执行垃圾回收任务的线程数量或其他与垃圾回收相关的参数。相比之下,在第2章中介绍的自适应代码生成只会优化热方法,并留下冷方法,直到它们 "变热",或由后续阶段的优化模块处理。

3.1.3 自动内存管理的缺点

对自动内存管理的争议主要集中在它会降低应用程序的执行效率,对某些应用来说是无法接受的,而之所以会降低执行效率,是因为它使得应用程序的响应时间带有高度的不确定性。为了避免这个问题,JVM做了大量的优化工作以达到期望的性能。

事实上,将内存管理的任务交由运行时负责确实会降低效率,但现在这已经不是什么大问题了,至少对那些写得好的程序来说的确是这样。

其实,影响垃圾回收器工作时间的主要因素是堆中存活对象的总数量,而不是堆的大小,如果存活对象特别多的话,啥算法都不好使。如果程序员手动管理内存,处理大量存活对象时可能问题会少些,但实际编码的时候,难以保证绝不犯错,况且"人肉垃圾回收"未必就比内存管理系统干得好。

确实,使用垃圾回收系统仍然可能会产生内存泄露。如果应用程序中错误的保存了很多本应被回收掉的对象的引用,这些对象就会被认为是存活对象,也就不会被回收。常见的内存泄露的例子就是对缓存的错误实现,例如java.util.HashMap,即便将所有对象都放到HashMap中,它也不会抛异常,但却会造成内存泄露,因为系统无法判断出HashMap中持有的某个对象其实已经没用了,只是被错误的保存了引用而已。